package com.young.es.client;

import com.sun.istack.internal.NotNull;
import com.young.es.client.config.ElasticsearchClientConfig;
import com.young.es.client.model.DocumentModel;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.util.StringUtils;

import java.io.IOException;

/**
 * RestHighLevelClient Demo用例
 *
 * @author ：<a href="mailto:youngkun2016@163.com">young</a>
 * @date ：Created in 2020/12/3
 */
public class ElasticsearchClientApplication {
    private static String index = "es-client";

    private static AnnotationConfigApplicationContext context;

    private static RestHighLevelClient restHighLevelClient;

    public static void main(String[] args) {
        context = new AnnotationConfigApplicationContext();
        context.register(ElasticsearchClientConfig.class);
        context.refresh();
        restHighLevelClient = context.getBean(RestHighLevelClient.class);
        //indexGet();
        //createIndexTest();
        //addDataTest();
        //searchTest();
        searchRequest(1, "name-01", null);
        //todo
        context.close();
    }

    /**
     * 索引存在判断
     *
     * @return
     */
    private static boolean indexGet() {
        GetIndexRequest request = new GetIndexRequest(index);
        //<1> 是返回本地信息还是从主节点检索状态
        request.local(false);
        //<2> 返回结果为适合人类的格式
        request.humanReadable(true);
        try {
            boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
            System.out.printf("index存在：{%s}", exists);
            return exists;
        } catch (Exception e) {
            System.out.printf(e.getMessage());
            return false;
        }
    }

    /**
     * 创建索引测试-测试成功
     */
    private static void createIndexTest() {
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);

        //创建索引时可以设置与之相关的 特定配置
        // index.number_of_shards 分片数
        // index.number_of_replicas 备份数
        createIndexRequest.settings(Settings.builder()
                .put("index.number_of_shards", 3)
                .put("index.number_of_replicas", 2)
        );
        //创建文档类型映射
        //类型映射，需要的是一个JSON字符串
        createIndexRequest.mapping(DocumentModel.getDocumentStruct().toJSONString(), XContentType.JSON
        );
        //可选参数
        //超时,等待所有节点被确认(使用TimeValue方式)
        createIndexRequest.setTimeout(TimeValue.timeValueMinutes(1));

        try {
            //同步执行
            CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            //返回的CreateIndexResponse允许检索有关执行的操作的信息，如下所示：
            //指示是否所有节点都已确认请求
            boolean acknowledged = createIndexResponse.isAcknowledged();
            //指示是否在超时之前为索引中的每个分片启动了必需的分片副本数
            boolean shardsAcknowledged = createIndexResponse.isShardsAcknowledged();
            System.out.println("acknowledged:" + acknowledged);
            System.out.println("shardsAcknowledged:" + shardsAcknowledged);
            System.out.println(createIndexResponse.index());
        } catch (IOException e) {
            System.out.printf(e.getMessage());
        } catch (ElasticsearchStatusException e) {
            System.out.printf(e.getMessage());
        }
        try {
            //关闭客户端链接
            restHighLevelClient.close();
        } catch (IOException e) {
            System.out.printf(e.getMessage());
        }
    }

    /**
     * 添加数据测试方法
     */
    private static void addDataTest() {
        IndexRequest request = new IndexRequest(index);
        try {
            for (int cur = 2, len = 8; cur < len; cur++) {
                request.source(DocumentModel.getOneData(Long.valueOf(cur), "name-0" + cur, "content-01" + cur), XContentType.JSON);
                IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
                System.out.printf("添加结果：{%s}\n", indexResponse.getResult());
                System.out.printf("添加状态：{%s}\n", indexResponse.status());
            }
        } catch (Exception e) {
            System.out.printf(e.getMessage());
        }
    }

    /**
     * 根据索引和文件ID查询
     */
    private static void searchTest() {
        GetRequest request = new GetRequest(index, "1");
        //当针对不存在的索引执行获取请求时，响应404状态码，将引发IOException，需要按如下方式处理：
        GetResponse documentFields;
        try {
            documentFields = restHighLevelClient.get(request, RequestOptions.DEFAULT);
            System.out.printf(documentFields.toString());
        } catch (IOException e) {
            System.out.printf(e.getMessage());
            ////处理因为索引不存在而抛出的异常情况
        } catch (ElasticsearchStatusException e) {
            System.out.printf(e.getMessage());
        }
        try {
            restHighLevelClient.close();
        } catch (IOException e) {
            System.out.printf(e.getMessage());
        }

    }

    /**
     * 根据文档属性查找
     */
    public static void searchRequest(@NotNull long id, String name, String content) {
        //创建一个空的SearchRequest，并像常规搜索一样填充它,参数是指定索引
        SearchRequest request = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.filter(QueryBuilders.matchPhraseQuery("properties.id", id));
        if (!StringUtils.isEmpty(name)) {
            boolQueryBuilder.filter(QueryBuilders.matchPhraseQuery("properties.name", name));
        }
        if (!StringUtils.isEmpty(content)) {
            boolQueryBuilder.filter(QueryBuilders.matchPhraseQuery("properties.content", content));
        }
        searchSourceBuilder.query(boolQueryBuilder);
        request.source(searchSourceBuilder);

        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            SearchHits searchHits = response.getHits();
            SearchHit[] result = searchHits.getHits();
            System.out.printf("查询结果：{%s}\n", response.getHits().getTotalHits().value);
            for (SearchHit searchHit : result) {
                System.out.println(searchHit.getSourceAsMap());
            }
            System.out.println();
        } catch (IOException e) {
            System.out.printf(e.getMessage());
        }
    }


}
